package com.chao.common.config.initialize;

import com.chao.common.codeGeneration.generation.JdbcDao;
import com.chao.common.util.ListUtile;
import com.chao.common.util.StrUtile;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 项目启动前校验数据库表结构和初始数据
 */
@Service
public class Initialize {

    private static final Logger log = LoggerFactory.getLogger(Initialize.class);

    public static JdbcDao toJdbc()  {
        try {
            File file = new File(System.getProperty("user.dir")+File.separator+"application.yml");
            InputStream applicationYml;
            if(file.exists()){
                applicationYml = new FileInputStream(file);
            }else {
                applicationYml = Initialize.class.getClassLoader().getResourceAsStream(File.separator+"application.yml");
            }
            if(null == applicationYml){
                return null;
            }
            String[] app = IOUtils.toString(applicationYml, StandardCharsets.UTF_8).split("\n");
            for (int i = 0; i < app.length; i++) {
                if(app[i].contains("datasource")){
                    String url = app[i+1].substring(app[i+1].indexOf(":")+1).trim();
                    // 解析获取库名
                    String db1 = url.split("\\?")[0];
                    String db = db1.substring(db1.lastIndexOf("/")+1);

                    String username = app[i+2].substring(app[i+2].indexOf(":")+1).trim();
                    String password = app[i+3].substring(app[i+3].indexOf(":")+1).trim();
                    return new JdbcDao(url.replace(db,"information_schema"),username,password,db);
                }
            }
        } catch (IOException e) {
            log.error("数据库连接失败",e);
        }
        return null;
    }

    public static void init() {
        try {
            long time = System.currentTimeMillis();
            // 获取初始化脚本
            InputStream tableStream = Initialize.class.getClassLoader().getResourceAsStream("data.sql");
            if(null == tableStream){
                log.info("数据库校验,未找到脚本");
                return;
            }

            // 连接数据库
            JdbcDao jdbc = toJdbc();
            if(null == jdbc){
                log.info("数据库连接失败!");
                return;
            }

            String[] newTables = IOUtils.toString(tableStream, StandardCharsets.UTF_8).split("\n");
            // 判断是否需要初始化数据库
            List<String> tables = jdbc.selectTableName(jdbc.getDb());
            if(ListUtile.isEmpty(tables)){
                // 创建数据库
                jdbc.createDatabase("create database "+jdbc.getDb()+";");
                jdbc.createDatabase("USE "+jdbc.getDb()+";");
                // 建表
                for (String newTable : newTables) {
                    if (!StrUtile.isEmpty(newTable)) {
                        jdbc.createDatabase(newTable);
                    }
                }
                log.info("初始化数据库,执行脚本{}条",newTables.length);
            }else { // 判断表结构是否有修改
                jdbc.createDatabase("USE "+jdbc.getDb()+";");
                List<String> ySql = new ArrayList<>();
                for (String table : tables) {
                    if (!StrUtile.isEmpty(table)) {
                        ySql.add(jdbc.selectTableStructure(jdbc.getDb(),table));
                    }
                }
                // 获取修改脚本并执行
                List<String> editSql = addEditSql(tables,ySql,newTables);
                if(!ListUtile.isEmpty(editSql)){
                    editSql.forEach(jdbc::createDatabase);
                }
                log.info("检索数据库,执行变更脚本{}条",editSql.size());
            }

            // 重置菜单按钮数据
//            InputStream menuStream = Initialize.class.getClassLoader().getResourceAsStream("menu.sql");
//            if(null != menuStream){
//                String[] newMenus = IOUtils.toString(menuStream, StandardCharsets.UTF_8).split("\n");
//                for (String newMenu : newMenus) {
//                    if (!StrUtile.isEmpty(newMenu)) {
//                        jdbc.createDatabase(newMenu);
//                    }
//                }
//            }
            jdbc.close();
            log.info("初始化脚本完成,耗时{}",(System.currentTimeMillis() - time));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 数据库结构比较
     * @param yTable 原表清单
     * @param ySql 原建表语句
     * @param sqls 现建表语句
     * @return 字段变更脚本
     */
    public static List<String> addEditSql(List<String> yTable,List<String> ySql,String[] sqls){
        // 表结构更新记录
        List<String> editSql = new ArrayList<>();
        //获取上个版本的表结构
        if(!ListUtile.isEmpty(ySql)){
            List<String> sqlList = Arrays.stream(sqls).filter(y -> !StrUtile.isEmpty(y) && y.contains("CREATE TABLE")).collect(Collectors.toList());
            // 解析现有表清单
            List<String> tableList = sqlList.stream().map(y -> y.substring(y.indexOf("TABLE `")+7,y.indexOf("` ("))).collect(Collectors.toList());
            for (int i = 0; i < tableList.size(); i++) {
                boolean f = false;
                for (int j = 0; j < yTable.size(); j++) {
                    if(StrUtile.isEmpty(ySql.get(j))){
                        continue;
                    }
                    // 判断表是否新增
                    if (tableList.get(i).equals(yTable.get(j))) {
                        //判断表结构是否有修改
                        if(!sqlList.get(i).equals(ySql.get(j))){
                            editSql.addAll(selectEditSql(ySql.get(j),sqlList.get(i)));
                        }
                        f = true;
                        break;
                    }
                }
                // 如果是新增表 添加到变更记录
                if(!f){
                    editSql.add(sqlList.get(i));
                }
            }
        }
        return editSql;
    }

    /**
     * 解析表变更记录
     * @param y 原表结构
     * @param x 现表结构
     * @return 变更语句
     */
    public static List<String> selectEditSql(String y,String x){
        String table = y.substring(y.indexOf("TABLE `")+7,y.indexOf("` ("));

        // 原表字段
        List<String> ys = Arrays.asList(y.substring(y.indexOf("(")+1,y.lastIndexOf(")")).split(","));
        // 现表字段
        List<String> xs = Arrays.asList(x.substring(x.indexOf("(")+1,x.lastIndexOf(")")).split(","));

        List<String> edit = new ArrayList<>();
        //解析原表字段
        List<String> yFields = ys.stream().filter(s -> s.contains("COMMENT")).map(s -> s.substring(s.indexOf("`") + 1, s.lastIndexOf("`"))).collect(Collectors.toList());
        //解析现表字段
        List<String> xFields = xs.stream().filter(s -> s.contains("COMMENT")).map(s -> s.substring(s.indexOf("`") + 1, s.lastIndexOf("`"))).collect(Collectors.toList());
        for (int i = 0, s = 0; i < ys.size() && s < xs.size();) {
            if(!xs.get(s).contains("COMMENT")){
                i++;s ++;
                continue;
            }
            // 判断是否新增字段
            if(!ys.contains(xs.get(s))){
                String[] y1 = ys.get(i).trim().split(" ");
                String[] x1 = xs.get(s).trim().split(" ");
                // 字段名和说明不一样 判断为新增字段
                if(!x1[0].equals(y1[0]) && !x1[x1.length-1].equals(y1[y1.length-1])){
                    edit.add("ALTER TABLE `"+table+"` ADD COLUMN "+xs.get(s) + " " + (s == 0? "FIRST":"AFTER `"+xFields.get(s-1)+"`"));
                    s ++;
                    continue;
                }else { // 否则判断为字段修改
                    edit.add("ALTER TABLE `"+table+"` CHANGE `"+yFields.get(i)+"`"+ xs.get(s) + " " + (s == 0? "FIRST":"AFTER `"+xFields.get(s-1)+"`"));
                }
            } else if(!xs.get(s).equals(ys.get(i))){ //判断是否位置变更
                edit.add("ALTER TABLE `"+table+"` CHANGE `"+xFields.get(s)+"`"+ xs.get(s) + " " + (s == 0? "FIRST":"AFTER `"+xFields.get(s-1)+"`"));
            }
            i++;s ++;
        }
        //解析原表索引
        List<String> yKey = ys.stream().filter(s -> s.contains("KEY") && !s.contains("PRIMARY")).map(s -> s.substring(s.indexOf("KEY `") + 5, s.lastIndexOf("` ("))).collect(Collectors.toList());
        //解析现表索引
        List<String> xKey = xs.stream().filter(s -> s.contains("KEY") && !s.contains("PRIMARY")).map(s -> s.substring(s.indexOf("KEY `") + 5, s.lastIndexOf("` ("))).collect(Collectors.toList());
        // 解析新增索引
        for (String xk : xKey) {
            // 原表没有 新表有 表示新增索引
            if(!yKey.contains(xk)){
                for (String xx:xs) {
                    if(xx.contains(xk)){
                        edit.add("ALTER TABLE `"+table+"` ADD "+xx.replace("KEY","INDEX").replace(",","")+";");
                    }
                }
            }
        }
        for (String yk : yKey) {
            // 原表有 新表没有 表示删除索引
            if(!xKey.contains(yk)){
                edit.add("ALTER TABLE `"+table+"` DROP INDEX `"+yk+"`;");
            }
        }
        return edit;
    }
}
